package com.peas.platform.common.core.configuration;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.StrUtil;
import com.peas.platform.common.core.annotation.Aop;
import com.peas.platform.common.core.aop.BaseAopExecution;
import com.peas.platform.common.core.constant.Constants;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;

/**
 * @author caizhh
 */
@Component
@Aspect
@Slf4j
public class AlphaAop {

    private final static String execution = "execution(public * " + Constants.BASE_PACKAGE + ".module..*.*(..)) && !execution(public * " + Constants.BASE_PACKAGE + ".module..adapter..*.*(..))";

    @Resource
    private ApplicationContext ac;

    @Pointcut(execution)
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before() {
        log.debug("AlphaAopController.before()");//2
    }

    @After("pointcut()")
    public void after() {
        log.debug("AlphaAopController.after()");//4
    }

    @AfterReturning("pointcut()")
    public void afterReturning() {
        log.debug("AlphaAopController.afterReturning()");//3
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        log.debug("AlphaAopController.afterThrowing()");
    }

    @SneakyThrows
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
        Object[] args = proceedingJoinPoint.getArgs();
        final Signature signature = proceedingJoinPoint.getSignature();
        final Class<?> clazz = signature.getDeclaringType();
        if (!(signature instanceof MethodSignature)) {
            return proceedingJoinPoint.proceed(args);
        }
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        boolean goon = false;
        Aop aspectOnClass = clazz.getAnnotation(Aop.class);
        Aop aspectOnMethod = method.getAnnotation(Aop.class);
        Class<?> superClazz = clazz.getSuperclass();
        Class<?>[] superInterfaces = clazz.getInterfaces();
        if (StrUtil.startWithIgnoreCase(clazz.getPackage().getName(), Constants.BASE_PACKAGE + ".module")) {
            goon = true;
        }
        if (StrUtil.equals(clazz.getPackage().getName(), "com.baomidou.mybatisplus.core.mapper")) {
            goon = true;
        }
        if (goon) {
            goon = isGoon(superClazz);
            for (Class<?> superInterface : superInterfaces) {
                goon = isGoon(superInterface);
            }
            if (aspectOnClass != null && !aspectOnClass.enabled()) {
                log.debug("[aspectOnClass]:aspectOnClass 不为 null 并且 aspectOnClass.save() == false");
                goon = false;
            }
            if (aspectOnMethod != null && !aspectOnMethod.enabled()) {
                log.debug("[aspectOnMethod]:aspectOnMethod 不为 null 并且 aspectOnMethod.save() == false");
                goon = false;
            }
        }
        if (!goon) {
            return proceedingJoinPoint.proceed(args);
        }
        StopWatch sw = new StopWatch();
        sw.start();
        Date start = DateUtil.date();
        Object result = null;
        String err = null;
        try {
            result = proceedingJoinPoint.proceed(args);
        } catch (Throwable e) {
            err = "执行异常:" + e.getMessage();
            throw e;
        } finally {
            sw.stop();
            Date end = DateUtil.date();
            final AlphaAopBean aab = new AlphaAopBean();
            aab.setArgs(args);
            aab.setResult(result);
            aab.setStart(start);
            aab.setEnd(end);
            aab.setDuration(sw.getTotalTimeMillis());
            aab.setTraceId(RequestContextUtil.getCurrentRequestId());
            aab.setSso_sessionid(RequestContextUtil.getCurrentSsoSessionId());
            aab.setUserId(RequestContextUtil.getCurrentUserId());
            aab.setUserCode(RequestContextUtil.getCurrentUserCode());
            aab.setAccount(RequestContextUtil.getCurrentAccount());
            aab.setClazz(clazz);
            aab.setMethod(method);
            aab.setError(err);
            Map<String, BaseAopExecution> map = ac.getBeansOfType(BaseAopExecution.class);
            if (CollUtil.isNotEmpty(map)) {
                for (Map.Entry<String, BaseAopExecution> entry : map.entrySet()) {
                    BaseAopExecution aae = entry.getValue();
                    aae.execution(aab);
                }
            }
        }
        return result;
    }

    private boolean isGoon(Class<?> superClass) {
        if (superClass == null)
            return true;
        switch (superClass.getName()) {
            case "com.peas.platform.framework.mybatisplus.interceptor.CustomInterceptor":
            case "com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor":
            case "com.peas.platform.common.core.interceptor.BaseInterceptor":
            case "com.peas.platform.common.core.interceptor.BaseFeignInterceptor":
                return false;
        }
        return true;
    }

}
